package com.xxl.job.executor.service.jobhandler;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.bizsys.service.ICreditAccountService;
import com.ruoyi.bizsys.service.ILoanIouService;
import com.ruoyi.bizsys.service.IRepaymentOrderService;
import com.ruoyi.business.service.IBRepaymentService;
import com.ruoyi.business.service.IWalletService;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.executor.constants.JobConstants;
import com.xxl.job.executor.service.WalletDebitService;
import com.xxl.job.executor.service.thread.WalletBatchDebitThread;
import com.xxl.job.executor.utils.GzipUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Component
public class WalletBatchDebitJob {

    @Autowired
    ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    ILoanIouService loanIouService;

    @Autowired
    IBRepaymentService repaymentService;

    @Autowired
    IRepaymentOrderService repaymentOrderService;

    @Autowired
    ICreditAccountService creditAccountService;

    @Autowired
    RedisCache redisCache;

    @Autowired
    IWalletService walletService;

    @Autowired
    WalletDebitService walletDebitService;

    @XxlJob("walletBatchDebitJobHandler")
    public void execute() throws InterruptedException {
        XxlJobHelper.log("钱包批量代扣 Start >>>>>>>>>>");

        String params = XxlJobHelper.getJobParam();
        if (StringUtils.isNotBlank(params)) {
            log.info("待发起钱包代扣处理，指定客户为：{}", params);
            List<String> custNos = new ArrayList<>(Arrays.asList(params.split(",")));
            CountDownLatch countDown = new CountDownLatch(custNos.size());
            threadPoolTaskExecutor.submit(new WalletBatchDebitThread(walletDebitService, custNos, countDown));
            countDown.await();
            custNos.clear();
            XxlJobHelper.log("钱包批量代扣 End >>>>>>>>>>");
            return;
        }

        // 查询有余额的钱包客户
        JSONObject json = walletService.getCustomersWithBalance();
        String custNos = GzipUtils.uncompressToString(json.getBytes("custNos"));
        if (StringUtils.isBlank(custNos)) {
            log.info("钱包批量代扣 End >>>>>>>>>> 钱包有余额客户不存在，钱包批量代扣没有记录需要执行");
            return;
        }

        log.info("存在余额客户长度：{}", custNos.length());

        // 查询到期及逾期借据客户
        List<String> dueIouCustNos = loanIouService.getDueIouCustNo();
        if (CollectionUtils.isEmpty(dueIouCustNos)) {
            log.info("钱包批量代扣 End >>>>>>>>>> 没有客户借据到期，钱包批量代扣没有记录需要执行");
            return;
        }

        // 求两个集合交集
        List<String> withBalanceCustNos = new ArrayList<>(Arrays.asList(custNos.split(",")));
        List<String> pendingCustNos = withBalanceCustNos.stream().filter(dueIouCustNos::contains).collect(Collectors.toList());
        log.info("存在余额客户数：{}，到期客户数：{}，待处理客户数：{}", withBalanceCustNos.size(), dueIouCustNos.size(), pendingCustNos.size());

        if (pendingCustNos.size() <= 0) {
            log.info("钱包批量代扣 End >>>>>>>>>> 钱包批量代扣没有记录需要执行");
            return;
        }

        // 拆分多个子list
        AtomicInteger count = new AtomicInteger();
        CountDownLatch countDown = new CountDownLatch(pendingCustNos.size());
        int limit = countStep(pendingCustNos.size());
        List<List<String>> splitList = Stream.iterate(0, n -> n + 1).limit(limit).parallel().map(a -> pendingCustNos.stream().skip(a * JobConstants.MAX_LIST_SIZE).limit(JobConstants.MAX_LIST_SIZE).parallel().collect(Collectors.toList())).collect(Collectors.toList());

        log.info(">>> 待处理客户分 {} 批处理", splitList.size());

        splitList.stream().forEach(custList -> {
            count.getAndIncrement();
            log.info(">>> 当前处理第 {} 个集合，数量为：{}", count.get(), custList.size());

            Future task = threadPoolTaskExecutor.submit(new WalletBatchDebitThread(walletDebitService, custList, countDown));

            try {
                Thread.sleep(50L);
            } catch (InterruptedException e) {
                log.error(" Thread.sleep Exception：", e);
            }
        });

        if (Thread.interrupted()) {
            XxlJobHelper.log("任务响应中断请求，停止执行");
            pendingCustNos.clear();
            dueIouCustNos.clear();
            withBalanceCustNos.clear();

            throw new InterruptedException();
        }

        try {
            log.debug("countDown remain = {}", countDown.getCount());
            countDown.await();

            pendingCustNos.clear();
            dueIouCustNos.clear();
            withBalanceCustNos.clear();
        } catch (Exception e) {
            log.error("WalletBatchDebitJobHandler error ... ", e);
            XxlJobHelper.handleFail("WalletBatchDebitJobHandler error ...");
        }

        XxlJobHelper.log("钱包批量代扣 End >>>>>>>>>>");
    }

    @XxlJob("walletDebitIouJobHandler")
    public void walletDebitIou() {
        XxlJobHelper.log("钱包代扣指定借据 Start >>>>>>>>>>");

        String params = XxlJobHelper.getJobParam();
        if (StringUtils.isBlank(params)) {
            XxlJobHelper.log("钱包代扣指定借据 End >>>>>>>>>> 没有记录待处理");
            return;
        }

        List<String> loanSerialNos = new ArrayList<>(Arrays.asList(params.split(",")));
        log.info("待处理借据为：{}", loanSerialNos);
        walletDebitService.walletDebitIou(loanSerialNos);

        XxlJobHelper.log("钱包代扣指定借据 End >>>>>>>>>> {}", loanSerialNos);
    }

    /**
     * 计算切分次数
     */
    private static Integer countStep(Integer size) {
        return (size + JobConstants.MAX_LIST_SIZE - 1) / JobConstants.MAX_LIST_SIZE;
    }
}
